#include "linux/of_irq.h"
#include "linux/slab.h"
#include "linux/device.h"
#include "linux/errno.h"
#include "linux/platform_device.h"
#include "linux/printk.h"
#include "linux/of_device.h"

#define to_platform_driver(drv) container_of((drv), struct platform_driver, driver)
#define to_platform_device(dev) container_of((dev), struct platform_device, dev)

struct bus_type platform_bus_type = {
    .name = "platform",
};

struct device platform_bus = {
    .init_name = "platform",
    .bus = &platform_bus_type,
};

static struct platform_device_id *platform_match_id(struct platform_device_id *id, struct platform_device *pdev)
{
    return NULL;
}

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev;
    struct platform_driver *pdrv;

    pdev = to_platform_device(dev);
    pdrv = to_platform_driver(drv);

    if (of_driver_match_device(dev, drv))
        return 1;

    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    return (strcmp(pdev->name, drv->name) == 0);
}

int platform_bus_init(void)
{

	platform_bus_type.name = "platform";
	platform_bus_type.match = platform_match;

    return bus_register(&platform_bus_type);
}

int platform_drv_probe(struct device *dev)
{
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(dev->driver);

    if (pdrv->probe)
        return pdrv->probe(pdev);
    
    return -ENODEV; // No probe function found
}


struct platform_device *platform_device_alloc( char *name, int id)
{
    struct platform_device *pdev = NULL;

    pdev = kmalloc(sizeof (struct platform_device), GFP_KERNEL);
    if (!pdev)
        return NULL;

    pdev->id    = id;
    pdev->name  = name;

    device_initialize(&pdev->dev);

    return pdev;
}

void platform_device_put(struct platform_device *pdev)
{
	//if (pdev)
		//put_device(&pdev->dev);
}

static int platform_drv_remove(struct device *dev)
{
    struct platform_driver *pdrv;
    struct platform_device *pdev;

    pdev = to_platform_device(dev);
    pdrv = to_platform_driver(dev->driver);

    if (!pdrv->remove)
        return -EINVAL;

    return pdrv->remove(pdev);
}

unsigned long resource_type(const struct resource *res)
{
	return res->flags & IORESOURCE_TYPE_BITS;
}

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
{
	int i;

	for (i = 0; i < dev->num_resources; i++) {
		struct resource *r = &dev->resource[i];

		if (type == resource_type(r) && num-- == 0)
			return r;
	}
	return NULL;
}


int __platform_driver_register(struct platform_driver *drv, void *owner)
{
    drv->driver.bus = &platform_bus_type;
    drv->driver.probe = platform_drv_probe;
    drv->driver.remove = platform_drv_remove;

    return driver_register(&drv->driver);
}   

int platform_driver_register(struct platform_driver *drv)
{
    return __platform_driver_register(drv, NULL);
}

void platform_driver_unregister(struct platform_driver *pdrv)
{
    driver_unregister(&pdrv->driver);
}

static int platform_device_add(struct platform_device *pdev)
{
   if (!pdev)
       return -EINVAL;

   pdev->dev.bus = &platform_bus_type;

   return device_register(&pdev->dev);
}


int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    return platform_device_add(pdev);
}

int platform_device_unregister(struct platform_device *pdev)
{
   return device_unregister(&pdev->dev);
}



int platform_get_irq(struct platform_device *dev, unsigned int num)
{
    struct resource *r;
    if (dev->dev.of_node)
    {
        int ret;

        ret = of_irq_get(dev->dev.of_node, num);
        if (ret > 0)
            return ret;
    }

    r = platform_get_resource(dev, IORESOURCE_IRQ, num);
    if (r && r->flags & IORESOURCE_BITS)
    {

    }

    return r ? r->start : -ENXIO;
}